/**
 *@File main.cpp
 *
 *@Author: Bob
 *
 *@Create Time: 2017-1-17 10:36:07
 *
 *@Last Modify: Bob
 *
 *@Last Modify Time: 2017-1-17 10:36:07
 *
 *@Description:
 *
 *
 */

#include <iostream>
#include <string>
#include <mach-o/dyld.h> // for _NSGetExecutablePath()

#include <pwd.h> // for getpwuid(getuid())

#include <unistd.h> // for gethostname
#include <ifaddrs.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <sys/utsname.h> // for uname
#include <stdio.h>

#include <sys/ioctl.h>
#include <uuid/uuid.h>

#include <sstream>
#include <fstream>
#include "xml2json.hpp"
#include "rapidjson/document.h"
#include "rapidjson/stringbuffer.h"
#include <rapidxml/rapidxml.hpp>
//#include <rapidxml/rapidxml_iterators.hpp>
#include <rapidxml/rapidxml_print.hpp>
#include <rapidxml/rapidxml_utils.hpp>

#include "Add.hpp"
#include "Sub.hpp"


#define MAX_PATH_LENGTH (1024)
#define MAX_SIZE (256)


std::string GetExecutablePath();
std::string GetAppFatherPath();
void ShowIPAddress();
void Calc();
void ShowPath();
void ShowHostName();
void ShowUserName();


std::string GetExecutablePath()
{
    std::string strPath;
    char szPath[MAX_PATH_LENGTH] = { 0 };
    unsigned int uSize = MAX_PATH_LENGTH;
    
    if (_NSGetExecutablePath(szPath, &uSize) == 0)
    {
        strPath = szPath;
    }
    
    return strPath;
}


std::string GetAppFatherPath()
{
    std::string strPath = GetExecutablePath();
    if (!strPath.empty())
    {
        std::string::size_type pos = strPath.rfind("/");
        if (pos != std::string::npos)
        {
            strPath = strPath.substr(0, pos);
        }
        else
        {
            strPath = "";
        }
    }
    
    return strPath;
}


void ShowIPAddress()
{
    struct ifaddrs* pIfAddrStruct = NULL;
    struct ifaddrs* pIfA = NULL;
    void *pAddr = NULL;
    std::string strIfaName;
    std::string strAddress;

    if (getifaddrs(&pIfAddrStruct) != -1)
    {
        for (pIfA = pIfAddrStruct; pIfA != NULL; pIfA = pIfA->ifa_next)
        {
            if (pIfA->ifa_addr == NULL)
            {
                continue;
            }
            
            if (pIfA->ifa_addr->sa_family == AF_INET)
            {
                pAddr = &((struct sockaddr_in *)pIfA->ifa_addr)->sin_addr;
                char szAddressBuf[INET_ADDRSTRLEN] = { 0 };
                if (inet_ntop(AF_INET, pAddr, szAddressBuf, INET_ADDRSTRLEN))
                {
                    strIfaName = pIfA->ifa_name;
                    strAddress = szAddressBuf;
                    
                    if (strAddress.compare("127.0.0.1") == 0)
                    {
                        continue;
                    }
                    
                    std::cout << strIfaName << " IPV4 is : " << strAddress << std::endl;
                }
            }
#if 0
            else if (pIfA->ifa_addr->sa_family == AF_INET6)
            {
                pAddr = &((struct sockaddr_in *)pIfA->ifa_addr)->sin_addr;
                char szAddressBuf[INET6_ADDRSTRLEN] = { 0 };
                if (inet_ntop(AF_INET6, pAddr, szAddressBuf, INET6_ADDRSTRLEN))
                {
                    strIfaName = pIfA->ifa_name;
                    strAddress = szAddressBuf;
                    std::cout << strIfaName << " IPV6 is : " << strAddress << std::endl;
                }
            }
#endif
        }
        
        if (pIfAddrStruct != NULL)
        {
            freeifaddrs(pIfAddrStruct);
            pIfAddrStruct = NULL;
        }
    }
}



void Calc()
{
    std::cout << "This is a Demo for Dynamic library" << std::endl;
    int a = 5, b = 3;
    Add add;
    Sub sub;
    std::cout << a << " + " << b << " = " << add.Sum(a, b) << std::endl;
    std::cout << a << " - " << b << " = " << sub.Minus(a, b) << std::endl;
}



void ShowPath()
{
    std::string strPath = GetExecutablePath();
    if (!strPath.empty())
    {
        std::cout << "ExecutablePath:" << GetExecutablePath() << std::endl;
    }
    
    strPath = GetAppFatherPath();
    if (!strPath.empty())
    {
        std::cout << "AppFatherPath:" << GetAppFatherPath() << std::endl;
    }
}



void ShowHostName()
{
    char szHostName[256] = { 0 };
    if (gethostname(szHostName, sizeof(szHostName)) == 0)
    {
        std::string str = szHostName;
        std::cout << "HostName: " << str << std::endl;
    }
}


void ShowUserName()
{
    std::string strUser = getlogin();
    if (!strUser.empty())
    {
        std::cout << "UserName: " << strUser << std::endl;
    }
    
    struct passwd *pwent = getpwuid(getuid());
    std::string strUsers = pwent->pw_name;
    if (!strUsers.empty())
    {
        std::cout << "UserName: " << strUsers << std::endl;
    }

}


void ShowUname()
{
    struct utsname stUname;
    if (uname(&stUname) != -1)
    {
        std::cout << "获取当前系统的信息如下: " <<std::endl;
        std::cout << "sysname(kernel): " << stUname.sysname <<std::endl;
        std::cout << "nodename: " << stUname.nodename <<std::endl;
        std::cout << "kernel version: " << stUname.version <<std::endl;
        std::cout << "machine(architecture): " << stUname.machine <<std::endl;
    }
    
}


void ShowRandomGuid()
{
    uuid_t Uuid;
    char szUuidBuf[36] = { 0 };
    uuid_generate(Uuid);
    uuid_unparse(Uuid, szUuidBuf);
    std::string strUuid = szUuidBuf;
    std::cout << "RandomGuuid: " << strUuid << std::endl;
}


void ShowArraySize()
{
    char szBuffer[MAX_SIZE] = {0};
    std::cout << "sizeof(szBuffer) : " << sizeof(szBuffer) << std::endl;
}



// Mac OSX下的版本信息文件 /System/Library/CoreServices/SystemVersion.plist
std::string GetMacOSVersion(std::string strFilePath)
{
    std::string strMacOSVersion;
    std::string strTmp;
    rapidxml::file<> docFile(strFilePath.c_str());
    rapidxml::xml_document<> doc;
    doc.parse<0>(docFile.data());
    
    rapidxml::xml_node<> *pRootNode = doc.first_node(); //plist
    if (pRootNode != NULL)
    {
        rapidxml::xml_node<> *pdictNode = pRootNode->first_node(); // dict
        if (pdictNode != NULL)
        {
            pdictNode = pdictNode->first_node();
            for (; pdictNode != NULL; pdictNode = pdictNode->next_sibling())
            {
                strTmp = pdictNode->value();
                if (strTmp.compare("ProductName") == 0)
                {
                    if (pdictNode->next_sibling() != NULL)
                    {
                        strMacOSVersion += pdictNode->next_sibling()->value();
                    }
                }
                else if (strTmp.compare("ProductUserVisibleVersion") == 0)
                {
                    if (pdictNode->next_sibling() != NULL)
                    {
                        strMacOSVersion = strMacOSVersion + " " + pdictNode->next_sibling()->value();
                        break;
                    }
                }
            }
        }
    }
    
    return strMacOSVersion;
}


std::string GetMacOSXVersion(const std::string strSystemVersionFilePath)
{
    std::string strVersion;
    std::ostringstream oss;
    std::ifstream inFile(strSystemVersionFilePath, std::ios::in | std::ios::binary);
    if (inFile.is_open())
    {
        oss << inFile.rdbuf();
        inFile.close();
        
        std::string strJson = xml2json(oss.str().c_str());
        rapidjson::Document doc;
        doc.Parse(strJson.c_str());
        if (!doc.HasParseError() && doc.IsObject())
        {
            if (doc.HasMember("plist"))
            {
                if (doc["plist"].IsObject() && doc["plist"].HasMember("dict"))
                {
                    rapidjson::SizeType indexProductName = -1;
                    rapidjson::SizeType indexVersion = -1;
                    std::string strTmp;
                    rapidjson::Value& value = doc["plist"]["dict"];
                    if (value.IsObject() && value.HasMember("key") && value["key"].IsArray())
                    {
                        rapidjson::Value& keyArr = doc["plist"]["dict"]["key"];
                        for (rapidjson::SizeType index = 0; index < keyArr.Size(); index++)
                        {
                            if (keyArr[index].IsString())
                            {
                                strTmp = keyArr[index].GetString();
                                if (strTmp == "ProductName")
                                {
                                    indexProductName = index;
                                }
                                
                                if (strTmp == "ProductUserVisibleVersion")
                                {
                                    indexVersion = index;
                                    if (indexProductName != -1)
                                    {
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    
                    if (value.IsObject() && value.HasMember("string") && value["string"].IsArray())
                    {
                        if ((indexProductName != -1) && (indexVersion != -1))
                        {
                            if (value["string"][indexProductName].IsString() &&
                                value["string"][indexVersion].IsString())
                            {
                                strVersion = value["string"][indexProductName].GetString();
                                strVersion += " ";
                                strVersion += value["string"][indexVersion].GetString();
                            }
                        }
                    }
                }
            }
        }
    }
    return strVersion;
}




#include <sys/param.h>
#include <sys/mount.h>

// Mac OS下获取磁盘信息
void GetDiskDriverSize()
{
    struct statfs tfs;
    statfs("/", &tfs);
    
    // each block size * total blocks
    long totalSize = tfs.f_bsize * tfs.f_blocks / 1024;
    // each block size * total free blocks
    long freeSize = tfs.f_bsize * tfs.f_bfree /1024;
    
    std::cout << "DiskTotalSize: " << totalSize << std::endl;
    std::cout << "DiskFreeSize: " << freeSize << std::endl;
}



#include <mach/vm_statistics.h>
#include <mach/mach_types.h>
#include <mach/mach_init.h>
#include <mach/mach_host.h>

// Mac OS下获取内存信息
void GetMemorySize()
{
    vm_size_t page_size;
    vm_statistics64_data_t vm_stats;
    mach_port_t mach_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_stats) / sizeof(natural_t);
    if (KERN_SUCCESS == host_page_size(mach_port, &page_size) &&
        KERN_SUCCESS == host_statistics64(mach_port, HOST_VM_INFO,
                                          (host_info64_t)&vm_stats, &host_size))
    {
        unsigned long memTotal = (vm_stats.free_count +
                                  vm_stats.active_count +
                                  vm_stats.inactive_count +
                                  vm_stats.wire_count) * page_size / 1024;
        unsigned long memAvailable = vm_stats.free_count * page_size / 1024;
        
        std::cout << "MemoryTotal: " << memTotal << std::endl;
        std::cout << "MemoryAvailable: " << memAvailable << std::endl;
    }

}


/*
 MacOS获取硬件UUID
 在Terminal中执行 ioreg -d2 -c IOPlatformExpertDevice可以得到如下结果
 
 +-o Root  <class IORegistryEntry, id 0x100000100, retain 15>
 +-o Macmini7,1  <class IOPlatformExpertDevice, id 0x100000112, regi$
 {
 "IOPlatformSystemSleepPolicy" = <534c505402001200001a12000000$
 "compatible" = <"Macmini7,1">
 "version" = <"1.0">
 "board-id" = <"Mac-35C5E08120C7EEAF">
 "IOInterruptSpecifiers" = (<0900000005000000>)
 "platform-feature" = <0300000000000000>
 "serial-number" = <473148560000000000000000004330375352333255$
 "IOInterruptControllers" = ("io-apic-0")
 "IOPlatformUUID" = "0F780ADB-6D2E-56D2-8365-0B4B7023F9AF"
 "target-type" = <"Mac">
 "clock-frequency" = <00e1f505>
 "manufacturer" = <"Apple Inc.">
 "IOPolledInterface" = "SMCPolledInterface is not serializable$
 "IOPlatformSerialNumber" = "C07SR32UG1HV"
 "system-type" = <01>
 "product-name" = <"Macmini7,1">
 "model" = <"Macmini7,1">
 "name" = <"/">
 "IOBusyInterest" = "IOCommand is not serializable"
 }
 */

#include <IOKit/IOKitLib.h> // 导入IOKit.framework
#include <CoreFoundation/CoreFoundation.h> // 导入CoreFoundation.framework

//获取MacOS的硬件UUID,需要导入IOKit.framework和CoreFoundation.framework
std::string GetHardwareUuid()
{
    std::string strHardwareUuid;
    
    io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
    if (platformExpert != IO_OBJECT_NULL)
    {
        CFTypeRef cfstring = IORegistryEntryCreateCFProperty(platformExpert, CFSTR(kIOPlatformUUIDKey), kCFAllocatorDefault, 0);
        if (cfstring)
        {
            strHardwareUuid = CFStringGetCStringPtr(static_cast<CFStringRef>(cfstring), kCFStringEncodingUTF8);
            CFRelease(cfstring);
            cfstring = NULL;
        }
        
        IOObjectRelease(platformExpert);
        platformExpert = NULL;
    }
    
    return strHardwareUuid;
}


//获取MacOS的系统序列号,需要导入IOKit.framework和CoreFoundation.framework
std::string GetSystemSerialNumber()
{
    std::string strSystemSerialNumber;
    io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
    if (platformExpert != IO_OBJECT_NULL)
    {
        CFTypeRef cfstring = IORegistryEntryCreateCFProperty(platformExpert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0);
        if (cfstring)
        {
            strSystemSerialNumber = CFStringGetCStringPtr(static_cast<CFStringRef>(cfstring), kCFStringEncodingUTF8);
            CFRelease(cfstring);
            cfstring = NULL;
        }
        
        IOObjectRelease(platformExpert);
        platformExpert = NULL;
    }
    
    return strSystemSerialNumber;
}


void ShowUuidAndSerialNumber()
{
    std::string strHardwareUuid = GetHardwareUuid();
    if (!strHardwareUuid.empty())
    {
        std::cout << "HardwareUuid: " << strHardwareUuid << std::endl;
    }
    
    std::string strSystemSerialNumber = GetSystemSerialNumber();
    if (!strSystemSerialNumber.empty())
    {
        std::cout << "SystemSerialNumber: " << strSystemSerialNumber << std::endl;
    }
}



int main(int argc, const char * argv[])
{
    Calc();
    std::cout << std::endl;
    
    ShowPath();
    std::cout << std::endl;
    
    ShowHostName();
    std::cout << std::endl;
    
    ShowUserName();
    std::cout << std::endl;
    
    ShowUname();
    std::cout << std::endl;
    
    ShowRandomGuid();
    std::cout << std::endl;
    
    ShowArraySize();
    std::cout << std::endl;
    
    ShowIPAddress();
    std::cout << std::endl;
    
    std::cout << "OS Version: " << GetMacOSVersion("/System/Library/CoreServices/SystemVersion.plist") << std::endl;
    std::cout << "OS Version: " << GetMacOSXVersion("/System/Library/CoreServices/SystemVersion.plist") << std::endl;
    std::cout << std::endl;
    
    GetDiskDriverSize();
    std::cout << std::endl;
    
    GetMemorySize();
    std::cout << std::endl;
    
    ShowUuidAndSerialNumber();
    
    return 0;
}